//	ID: 066498650
//	Exercise #: 2
//	By:	Roy Moore
//
//	Written:	19/12/2011
//	Environment : VS2010
//
//-tcpServer.cpp-
//
//*Please include "ws2_32.lib" in the library list.
#include "tcpServer.h"

struct SocketState sockets[MAX_SOCKETS]={0};
int socketsCount = 0;

void main() 
{
	cout << "*Web Server*" << endl;
	// Initialize Winsock (Windows Sockets).
	WSAData wsaData; 
	// Call WSAStartup and return its value as an integer and check for errors.
	if (NO_ERROR != WSAStartup(MAKEWORD(2,2), &wsaData))
	{
        cout<<"Web Server: Error at WSAStartup()\n";
		return;
	}
	// Create and bind a socket to an internet address.
    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	// Check for errors to ensure that the socket is a valid socket.
	if (INVALID_SOCKET == listenSocket)
	{
        cout<<"Web Server: Error at socket(): "<<WSAGetLastError()<<endl;
        WSACleanup();
        return;
	}
	// Create a sockaddr_in object called serverService. 
	sockaddr_in serverService;
    serverService.sin_family = AF_INET; 
	serverService.sin_addr.s_addr = INADDR_ANY;
	serverService.sin_port = htons(HTTP_PORT);
	// Bind the socket for client's requests.
    if (SOCKET_ERROR == bind(listenSocket, (SOCKADDR *) &serverService, sizeof(serverService))) 
	{
		cout<<"Web Server: Error at bind(): "<<WSAGetLastError()<<endl;
        closesocket(listenSocket);
		WSACleanup();
        return;
    }
	// Listen on the Socket for incoming connections.
    if (SOCKET_ERROR == listen(listenSocket, 5))
	{
		cout << "Web Server: Error at listen(): " << WSAGetLastError() << endl;
        closesocket(listenSocket);
		WSACleanup();
        return;
	}
	addSocket(listenSocket, LISTEN);
    // Accept connections and handles them one by one.
	while (true)
	{
		fd_set waitRecv;
		FD_ZERO(&waitRecv);
		for (int i = 0; i < MAX_SOCKETS; i++)
		{
			if ((sockets[i].recv == LISTEN) || (sockets[i].recv == RECEIVE))
				FD_SET(sockets[i].id, &waitRecv);
		}

		fd_set waitSend;
		FD_ZERO(&waitSend);
		for (int i = 0; i < MAX_SOCKETS; i++)
		{
			if (sockets[i].send == SEND)
				FD_SET(sockets[i].id, &waitSend);
		}

		// Wait for interesting event.
		int nfd;

		struct timeval timeout;
		timeout.tv_sec = TIMEOUT; //Set Timeout

		nfd = select(0, &waitRecv, &waitSend, NULL, &timeout);
		if (nfd == SOCKET_ERROR)
		{
			cout <<"Web Server: Error at select(): " << WSAGetLastError() << endl;
			WSACleanup();
			return;
		}

		for (int i = 0; i < MAX_SOCKETS && nfd > 0; i++)
		{
			if (FD_ISSET(sockets[i].id, &waitRecv))
			{
				nfd--;
				switch (sockets[i].recv)
				{
				case LISTEN:
					acceptConnection(i);
					break;

				case RECEIVE:
					receiveMessage(i);
					break;
				}
			}
		}

		for (int i = 0; i < MAX_SOCKETS && nfd > 0; i++)
		{
			if (FD_ISSET(sockets[i].id, &waitSend))
			{
				nfd--;
				switch (sockets[i].send)
				{
				case SEND:
					sendMessage(i);
					break;
				}
			}
		}
	}

	// Closing connections and Winsock.
	cout << "Web Server: Closing Connection.\n";
	closesocket(listenSocket);
	WSACleanup();
}

bool addSocket(SOCKET id, int what)
{
	for (int i = 0; i < MAX_SOCKETS; i++)
	{
		if (sockets[i].recv == EMPTY)
		{
			sockets[i].id = id;
			sockets[i].recv = what;
			sockets[i].send = IDLE;
			sockets[i].len = 0;
			socketsCount++;
			// Set the socket to be in non-blocking mode.
			unsigned long flag=1;
			if (ioctlsocket(sockets[i].id, FIONBIO, &flag) != 0)
			{
				cout<<"Web Server: Error at ioctlsocket(): "<<WSAGetLastError()<<endl;
			}

			return (true);
		}
	}

	return (false); //Full
}

void removeSocket(int index)
{
	sockets[index].recv = EMPTY;
	sockets[index].send = EMPTY;
	socketsCount--;
}

void acceptConnection(int index)
{
	SOCKET id = sockets[index].id;
	struct sockaddr_in from;		// Address of sending partner
	int fromLen = sizeof(from);

	SOCKET msgSocket = accept(id, (struct sockaddr *)&from, &fromLen);
	if (INVALID_SOCKET == msgSocket)
	{ 
		 cout << "Web Server: Error at accept(): " << WSAGetLastError() << endl; 		 
		 return;
	}
	cout << "Web Server: Client "<<inet_ntoa(from.sin_addr)<<":"<<ntohs(from.sin_port)<<" is connected." << endl;

	if (addSocket(msgSocket, RECEIVE) == false)
	{
		cout<<"\t\tToo many connections, dropped!\n";
		closesocket(id);
	}
	return;
}

void receiveMessage(int index)
{
	SOCKET msgSocket = sockets[index].id;

	int len = sockets[index].len;
	int bytesRecv = recv(msgSocket, &sockets[index].buffer[len], sizeof(sockets[index].buffer) - len, 0);
	
	if (SOCKET_ERROR == bytesRecv)
	{
		cout << "Web Server: Error at recv(): " << WSAGetLastError() << endl;
		closesocket(msgSocket);			
		removeSocket(index);
		return;
	}
	if (bytesRecv == 0) //Terminated
	{
		closesocket(msgSocket);			
		removeSocket(index);
		return;
	}
	else
	{
		sockets[index].buffer[len + bytesRecv] = '\0'; //add the null-terminating to make it a string
		cout<<"Web Server: Recieved: "<<bytesRecv<<" bytes of" << endl << "\""<<&sockets[index].buffer[len]<<"\"" << endl;
		sockets[index].len += bytesRecv;

		HTTPREQ method = getMethod(index);

		if (sockets[index].len > 0)
		{
			UpdateSocketToRequest(method, index, bytesRecv, len);
		}
	}
}

HTTPREQ getMethod(int index)
{
	string reqStr = sockets[index].buffer;
	reqStr = reqStr.substr(0, reqStr.find(CR_LF));
	reqStr = reqStr.substr(0,reqStr.find_first_of(" "));

	if(reqStr.compare("GET") == 0)
	{
		return GET;
	}
	else if(reqStr.compare("PUT") == 0)
	{
		return PUT;
	}
	else if(reqStr.compare("HEAD") == 0)
	{
		return HEAD;
	}
	else if(reqStr.compare("DELETE") == 0)
	{
		return DEL;
	}
	else if(reqStr.compare("TRACE") == 0)
	{
		return TRACE;
	}
	else
	{
		return NA;
	}
}

void UpdateSocketToRequest(HTTPREQ method, int index, int bytesRecv, int len)
{
	sockets[index].send = SEND;
	memcpy(sockets[index].buffer, &sockets[index].buffer[bytesRecv], sockets[index].len - bytesRecv);
	sockets[index].len -= bytesRecv;

	switch(method)
	{
		case GET:
			sockets[index].sendSubType = GET;
			break;
		case HEAD:
			sockets[index].sendSubType = HEAD;
			break;
		case DEL:
			sockets[index].sendSubType = DEL;
			break;
		case PUT:
			sockets[index].sendSubType = PUT;
			break;
		case TRACE:
			sockets[index].sendSubType = TRACE;
			break;
		default:
		cout<<"Web Server: Recieved: "<<bytesRecv<<" bytes of:\n "<<&sockets[index].buffer[len]<<" -NA msg-\n";
	}
}

void sendMessage(int index)
{
	SOCKET msgSocket = sockets[index].id;

	if (sockets[index].sendSubType == GET || sockets[index].sendSubType == HEAD)			
	{
		respondGet(index);
	}
	else if(sockets[index].sendSubType == DEL)		
	{
		respondDel(index);
	}
	else if(sockets[index].sendSubType == PUT)
	{
		respondPut(index);
	}
	else if(sockets[index].sendSubType == TRACE)
	{
		respondTrace(index);
	}
	if(sockets[index].len == 0)
	{
		sockets[index].send = IDLE;
	}
}

void respondGet(int index)
{
	string filePath = getFullPath(index); //Get Path
	int method = getMethod(index);
	SOCKET msgSocket = sockets[index].id;
	string msgStr;
	ifstream file;

	file.open(filePath.c_str(), ifstream::in); //File Open

	if(method == GET || method == HEAD)
	{	
		if(file.is_open())
		{
			file.seekg (0, ios::end);
			int fileSize = (int) file.tellg(); //Get File size
			file.seekg (0, ios::beg);

			HeaderBuilder(msgStr,OK,fileSize);

			if(method == GET)
			{
				string fileStr;

				while(file.good())
				{
					getline(file,fileStr);
					msgStr.append(fileStr);
					msgStr.append(CR_LF);
				}

				file.close();
			}
		}
		else //Error
		{
			string errStr = "File Not Found";
			errStr.append(CR_LF);
			HeaderBuilder(msgStr,NOT_FOUND,errStr.length());
			msgStr.append(errStr);
		}

		sendTcpMsg(msgSocket,msgStr);
	}
}

void respondDel(int index)
{
	string filePath = getFullPath(index);
	SOCKET msgSocket = sockets[index].id;
	string msgStr;

	if(remove(filePath.c_str()) == 0)
	{
		HeaderBuilder(msgStr,OK,0); //No body.
	}
	else
	{
		string errStr = "File Not Found";
		errStr.append(CR_LF);
		HeaderBuilder(msgStr,NOT_FOUND,errStr.length());
		msgStr.append(errStr);
	}

	sendTcpMsg(msgSocket,msgStr);
}

void respondPut(int index)
{
	string filePath = getFullPath(index);
	HTTPREQ method = getMethod(index);
	SOCKET msgSocket = sockets[index].id;
	string msgStr;
	ofstream file; 

	file.open(filePath.c_str(),ios::out); //Open file

	string bodyStr = sockets[index].buffer; 
	bodyStr = bodyStr.substr(bodyStr.find("\r\n\r\n")); //Find Body
	bodyStr = bodyStr.substr(4);

	if(file.is_open())
	{
		HeaderBuilder(msgStr,OK,0); //No body.
		file << bodyStr.c_str(); //Write
		msgStr.append(CR_LF);
		file.close(); //Close file
	}
	else
	{
		cout << "Web Server: Error creating "<<filePath<<endl;
		string errStr = "Error creating file";
		errStr.append(CR_LF);
		HeaderBuilder(msgStr,SERVER_ERROR,errStr.length());
		msgStr.append(errStr);
	}
	
	sendTcpMsg(msgSocket,msgStr);
}

void respondTrace(int index)
{
	//Echo
	string filePath = getFullPath(index);
	HTTPREQ method = getMethod(index);
	SOCKET msgSocket = sockets[index].id;
	string msgStr;
	string recivedMsgStr;

	recivedMsgStr = sockets[index].buffer;
	HeaderBuilder(msgStr,OK,recivedMsgStr.length());
	msgStr.append(CR_LF);
	msgStr.append(recivedMsgStr);
	msgStr.append(CR_LF);

	sendTcpMsg(msgSocket,msgStr);
}

string getFullPath(int index) 
{
	string reqStr = sockets[index].buffer; 
	reqStr = reqStr.substr(0, reqStr.find(CR_LF)); //Get request line

	if(reqStr.find("http://") != -1) //Full ("http://<domain>" prefix):
	{
		reqStr = reqStr.substr(reqStr.find("//"));
		reqStr = reqStr.substr(2);
		reqStr = reqStr.substr(reqStr.find("/"));
		return reqStr.substr(0,reqStr.find_first_of(" ")).substr(1);
	}
	else 
	{
		string msg = sockets[index].buffer;
		if(msg.find("Path") != -1) //("/<path>/<file>"):
		{	
			reqStr = msg.substr(msg.find("Path: ")); //"Path" prefix header:
			reqStr = reqStr.substr(0, reqStr.find(CR_LF));
			reqStr = reqStr.substr(6);
			return reqStr.substr(1);
		}
		else
		{	
			reqStr = reqStr.substr(reqStr.find("/")); //Request line w/o prefix:
			return reqStr.substr(0,reqStr.find_first_of(" ")).substr(1);
		}
	}
}

void HeaderBuilder(string& msg,HTTPRSP code,int len)
{
	char buffer[100];

	switch (code) //Add status line
	{
	case OK:
		msg.append("HTTP/1.1 200 OK");
		msg.append(CR_LF);
		break;
	case NOT_FOUND:
		msg.append("HTTP/1.1 404 Not Found");
		msg.append(CR_LF);
		break;
	case SERVER_ERROR:
		msg.append("HTTP/1.1 500 Internal Server Error");
		msg.append(CR_LF);
		break;
	}

	if (len != 0) //Add Body
	{
		msg.append("Content-Length :");
		msg.append(_itoa(len,buffer,10));
		msg.append(CR_LF);
	}

	msg.append("Content-Type : text/html");
	msg.append(CR_LF);
	msg.append("Connection: keep-alive");
	msg.append(CR_LF);
	msg.append(CR_LF);
}

void sendTcpMsg(SOCKET msgSocket,string msgStr)
{
	int bytesSent = send(msgSocket, msgStr.c_str(), (int)strlen(msgStr.c_str()), 0);

	if (SOCKET_ERROR == bytesSent)
	{
		cout << "Web Server: Error at send(): " << WSAGetLastError() << endl;	
		return;
	}

	cout<<"Web Server: Sent: "<<bytesSent<<"\\"<<strlen( msgStr.c_str())<<" bytes of:\n"<< msgStr.c_str()<<"\n";	
}